#include <glog/logging.h>
#include "Orion.h"
#include "HttpRequest.h"
#include "HttpResponse.h"
#include "IOBehavior.h"
#include "Utils.h"
#include "AsyncEventGenerator.h"
#include "AsyncEventGeneratorGroup.h"
#include "AbstractAsyncStream.h"
#include "SocketAcceptor.h"
#include "Coroutine.h"

USING_ORION_V2;

const std::string BIND_ADDRESS = "0.0.0.0";
const int BIND_PORT = 40443;
const int BACK_LOG = 1000;
const bool ALLOW_ADDRESS_REUSE = YES;
const int SOCKET_TIME_OUT_MS = 5 * 1000;

#ifdef OSX

const std::string SSL_CERT_PATH = "/Users/suzhen/hotelcloud.pujie88.com.crt";
const std::string SSL_KEY_PATH = "/Users/suzhen/hotelcloud.pujie88.com.key";

#else

const std::string SSL_CERT_PATH = "/home/suzhen/hotelcloud.pujie88.com.crt";
const std::string SSL_KEY_PATH = "/home/suzhen/hotelcloud.pujie88.com.key";

#endif

#define ENABLE_SSL 1

int main(int argc, char **arg) {
    Coroutine::GetCoroutineMgr();
    Coroutine::go([]() {
        LOG_I << "hello world";
    });
    //LOG(INFO) << "GOOGLE_PROTOBUF_VERSION " << GOOGLE_PROTOBUF_VERSION;
    //GOOGLE_PROTOBUF_VERIFY_VERSION;
#if ENABLE_SSL == 1
    SSL_library_init();
    OpenSSL_add_all_algorithms();
#endif
    //创建EventGeneratorGroup
    AsyncEventGeneratorGroup group(Utils::GetServerClusterSuggestSize());
    //定义IO行为
    IOBehavior ioBehavior;
    ioBehavior.setOnOpenCallBack([&](AbstractAsyncStream *s) {
        LOG_I << "On Open";
    });
    ioBehavior.setOnAcceptCallBack([&](AbstractAsyncStream *s) {
        s->setUserData(new HttpRequest);
        HttpRequest *req = static_cast<HttpRequest *>( s->getUserData());
        req->retain();
        group.getLoadBalanceGenerator()->submit(s);
    });
    ioBehavior.setOnClosedCallBack([](AbstractAsyncStream *s) {
        HttpRequest *req = static_cast<HttpRequest *>( s->getUserData());
        req->release();
        LOG_I << "On Closed";
    });
    //
    ioBehavior.setOnDataReadCallBack([](AbstractAsyncStream *s,
                                        const RawDataType *data,
                                        size_t sz) {
        if (s->isClosing()) {
            LOG_I << "CLOSING !!!!";
        }
        HttpRequest *req = static_cast<HttpRequest *>( s->getUserData());
        req->executeParse(data, sz);
        if (req->hasErr()) {
            LOG_I << "got error";
            //close socket
            s->closeAsync();
        } else if (req->hasDone()) {
            req->retain();
            s->retain();
            std::thread subThread([s, req]() {
                req->printDetails();
                HttpResponse rsp(200, "text/plain");
                rsp.writeBody(req->getBody());
                std::string httpRspTxt = rsp.buildHttpText();
                IOBuffer sendBuffer(httpRspTxt.data(),
                                    httpRspTxt.size(),
                                    false);
                s->sendAsync(std::move(sendBuffer))
                        .then([s, req]() {
                            //
                            s->closeAsync()
                                    .then([s, req]() {
                                        s->release();
                                    });
                        });
                req->release();
            });
            subThread.detach();
        }
    });
    //配置cfg
    Configuration cnf(BIND_ADDRESS,
                      BIND_PORT,
                      BACK_LOG,
                      ALLOW_ADDRESS_REUSE,
                      SOCKET_TIME_OUT_MS);
    //创建 SocketAcceptor 将其附加到 EventGenerator 上
    std::unique_ptr<SocketAcceptor> sAcceptor(new SocketAcceptor(std::move(cnf), ioBehavior
#if ENABLE_SSL == 1
            , TlsConf(SSL_CERT_PATH, SSL_KEY_PATH)
#else
            , TlsConf()
#endif
    ));
    LOG(INFO) << "app start";
    //选中 EventGeneratorGroup 中第一个 EventGenerator
    group.getAsyncEventGeneratorAt(0)->submit(sAcceptor.get());
    group.run().get();
    //tip: how to close acceptor and group ?
    // look
    // sAcceptor->closeAsync().get();
    // group.stop().get();
    //
    group.stopFuture().get();
    LOG_I << "CLOSED";
    return EXIT_SUCCESS;
}
